// 11/04/2009 Dennis
// ListBoxHeader and ListBoxItem used for ListBox
// ListBoxHeader has a label that is displayed and a width
// the width is in percent. If you need two headers, one with 70 and one with 30 percent
// use listbox.header.add(new ListBoxHeader("Title...", 70));
// and listbox.header.add(new ListBoxHeader("second header", 30));
class ListBoxHeader
{
  String label;
  int width;
  public ListBoxHeader(String label, int width)
  {
    this.label = label;
    this.width = width;
  }
}
class ListBoxItem
{
  ArrayList labels;
  public ListBoxItem()
  {
    labels = new ArrayList();
  }
}

// 11/04/2009 Dennis
// Control to draw a list of items like the detail view of windows explorer
// header is a list of ListBoxHeader and the size of the list will define the number of columns
// items is a list of ListBoxItem. All items MUST have as many labels as there are columns
class ListBox extends Control
{
  ArrayList items;
  ArrayList header;
  int selection;
  boolean mouseDown;
  boolean noHeader;
  int scrollbarheight;
  int scrollbararea;
  int scrollpos;
  int scrollClickY;
  boolean lockedScrollbar;
  int maxBarVal;
  int minBarVal;
  color barcolor;
  boolean wasOver = false;
  
  // 11/04/2009 Dennis
  public ListBox()
  {
    mouseDown = false;
    items = new ArrayList();
    header = new ArrayList();
    x = 0;
    y = 0;
    width = scrW;
    height = scrH - 100;
    noHeader = false;
    selection = -1;
    backcolor = color(255);
    forecolor = color(0);
    bordercolor = color(0);
    highlightcolor = color(#71b7e6);
    barcolor = color(#71b7e6);
    scrollpos = 0;
    scrollbarheight = 0;
  }
  
  // 11/04/2009 Dennis
  public void draw()
  {
    width = scrW;
    height = scrH - 100;
    fill(backcolor);
    noStroke();
    rect(getX(), getY(), width, height);
    boolean altColor = false;
    int padding = 5;
    int rowheight = fontsize + 2 * padding;
    int rowwidth = width - 20; // width - scrollbar
    int localx = getX();
    int localy = getY();
    textFont(font);
    fill(forecolor);
    scrollbararea = height - 5;
    if(items.size() > 0)
      scrollbarheight = scrollbararea * scrollbararea / (items.size() * rowheight);
    else scrollbarheight = scrollbararea;
    if(scrollbarheight > scrollbararea) scrollbarheight = scrollbararea;
    if(noHeader)
    {
      int i0 = (int)((float)scrollpos / scrollbararea * items.size());
      for(int i = i0; i < items.size(); i++)
      {
        altColor = !altColor;
        noStroke();
        fill(altColor ? color(255) : color(#ebebeb));
        rect(getX(), localy, rowwidth, rowheight);
        ListBoxItem lbi = (ListBoxItem)items.get(i);
        if(localy + rowheight >= getY() + height)
          break;
        if(selection == i)
        {
          noStroke();
          fill(mouseDown ? highlightcolor : color(#d0e4f0));
          rect(getX(), localy, rowwidth, rowheight);
        }
        fill(forecolor);
        text((String)lbi.labels.get(0), localx + padding, localy + padding + 3, rowwidth - 2 * padding, fontsize);
        localy += rowheight;
      }
    }
    else
    {
      line(localx, localy + rowheight, localx + rowwidth, localy + rowheight);
      int i0 = (int)((float)scrollpos / scrollbararea * items.size());
      noStroke();
      fill(0);
      rect(getX(), getY(), rowwidth, rowheight);
      for(int i = 0; i < header.size(); i++)
      {
        ListBoxHeader lbh = (ListBoxHeader)header.get(i);
        int columnwidth = (int)(lbh.width * rowwidth / 100);
        localy = getY();
        //stroke(bordercolor);
        //line(localx, localy, localx, localy + rowheight);
        fill(255);
        textFont(font1bold);
        text(lbh.label, localx + padding, localy + padding + 2, columnwidth - 2 * padding, fontsize);
        textFont(font1);
        
        for(int j = i0; j < items.size(); j++)
        {
          ListBoxItem lbi = (ListBoxItem)items.get(j);
          localy += rowheight;
          if(localy + rowheight >= getY() + height)
            break;
          if(i == 0)
          {
            altColor = !altColor;
            noStroke();
            fill(altColor ? color(255) : color(#ebebeb));
            rect(getX(), localy, rowwidth, rowheight);
          }
          if(selection == j && i == 0)
          {
            noStroke();
            fill(mouseDown ? highlightcolor : color(#d0e4f0));
            rect(getX(), localy, rowwidth, rowheight);
          }
          if(lbh.label == "Popularity")
          {
            int popularity = (Integer)lbi.labels.get(i);
            noStroke();
            fill(barcolor);
            int w;
            if(maxBarVal - minBarVal != 0)
              w = (columnwidth - 20) * (popularity - minBarVal) / (maxBarVal - minBarVal) + 20;
            else w = 0;
            if(popularity == 0)
              w = 0;
            rect(localx + 1, localy + 1, w - 2, rowheight - 2);
            fill(backcolor);
            text(""+popularity, localx + padding, localy + padding + 2, columnwidth - 2 * padding, fontsize);
          }
          else
          {
            fill(forecolor);
            text((String)lbi.labels.get(i), localx + padding, localy + padding + 2, columnwidth - 2 * padding, fontsize);
          }
        }
        localx += columnwidth;
      }
    }
    fill(bordercolor);
    stroke(1);
    line(getX() + rowwidth, getY(), getX() + rowwidth, getY() + height);
    noStroke();
    fill(selection == -2 ? mouseDown ? highlightcolor : bordercolor : color(200));
    rect(getX() + rowwidth + 3, getY() + 3 + scrollpos, 15, scrollbarheight);
  }
  
  // 11/04/2009 Dennis
  public boolean getEvent(int mx, int my, int mstate)
  {
    if(!wasOver && mstate != 0 && !lockedScrollbar)
      return false;
    wasOver = mouseOver(mx, my);
    if(mouseOver(mx, my))
    {
      if(mx < width - 20 && !lockedScrollbar)
      {
        selection = (my / (fontsize + 10)) + (int)((float)scrollpos / scrollbararea * items.size());
        if(!noHeader) selection--;
        if(selection < items.size() && selection >= 0)
          mouseCursor = 1;
        if(mstate == 2 && selection < items.size() && selection >= 0)
          return true;
      }
      else // over scrollbar
      {
        if(my >= scrollpos && my <= scrollpos + scrollbarheight || lockedScrollbar)
        {
          scroll(my, mstate);
        }
        else
          selection = -1;
      }
    }
    else
    {
      if(lockedScrollbar)
      {
        scroll(my, mstate);
      }
      selection = -1;
    }
    mouseDown = mstate == 1;
    return false;
  }
  
  // 18/04/2009 Dennis
  private void scroll(int my, int mstate)
  {
    if(mstate == 1 && !mouseDown)
    {
      scrollClickY = my - scrollpos;
      lockedScrollbar = true;
    }
    if(mstate != 1)
      lockedScrollbar = false;
    selection = -2;
    if(mouseDown)
      scrollpos = my - scrollClickY;
    if(scrollpos < 0) scrollpos = 0;
    else if(scrollpos > scrollbararea - scrollbarheight) scrollpos = scrollbararea - scrollbarheight;
  }
  
  // 14/04/2009 Dennis
  public ListBoxItem getSelection()
  {
    if(selection >= 0 && selection < items.size())
      return (ListBoxItem)items.get(selection);
    return null;
  }
  
  // 28/04/2009 Dennis
  public void sortPopularity()
  {
    int n = items.size();
    quicksort(0, n-1);
  }
  private void quicksort (int lo, int hi)
  {
      int i = lo, j = hi;
      ListBoxItem lbi1;
      lbi1 = (ListBoxItem)items.get((lo + hi) / 2);
      
      int x = (Integer)lbi1.labels.get(3);

      while (i <= j)
      {    
          while ((Integer)((ListBoxItem)items.get(i)).labels.get(3) > x) i++; 
          while ((Integer)((ListBoxItem)items.get(j)).labels.get(3) < x) j--;
          if (i <= j)
          {
              Collections.swap(items, i, j);
              i++;
              j--;
          }
      }
      if (lo < j) quicksort(lo, j);
      if (i < hi) quicksort(i, hi);
  }

}
